/*******************************************************************
 *
 * Copyright (C), 2022-2027, YouGan, All rights reserved.
 *
 * Author      : zhaoyang
 * Create Time : 2022-10-15
 * Version     : v0.1
 * Description : 
 *******************************************************************/

/*----------------------------Head file----------------------------*/
#include "gpio_i2c.h"
#include "nrf_gpio.h"
#include "nrf_delay.h"

#include "nrf_log.h"

/*----------------------------macro file---------------------------*/


/* Gensor I2C PIN 和address */
#define I2C_GSENSOR_PIN_SLC                	17      //p0.17	
#define I2C_GSENSOR_PIN_SDA                	20	    //p0.20


/*----------------------------type define--------------------------*/

/*----------------------------var define---------------------------*/

/*-------------------------func declarative------------------------*/

/*-----------------------------------------------------------------*/



#define     I2C_USE_7BIT_ADDR               //如果使用的从机地址是7Bit模式，则打开这个宏，否则注释掉这个宏
#define     I2C_DELAY               5      // I2C每个Bit之间的延时时间，延时越小I2C的速率越高


//下面这些宏要根据具体的平台进行调整。

#define     SW_I2C_SCL_LOW          nrf_gpio_pin_clear(gpio->SCL_Pin)                         // I2C SCL脚输出0
#define     SW_I2C_SCL_HIGH         nrf_gpio_pin_set(gpio->SCL_Pin)                           // I2C SCL脚输出1
#define     SW_I2C_SDA_LOW          nrf_gpio_pin_clear(gpio->SDA_Pin)                         // I2C SDA脚输出0
#define     SW_I2C_SDA_HIGH         nrf_gpio_pin_set(gpio->SDA_Pin)                           // I2C SDA脚输出1
#define     SW_I2C_SDA_INPUT        nrf_gpio_cfg_input(gpio->SDA_Pin, NRF_GPIO_PIN_PULLUP)    // 将SDA脚方向设置为输入
#define     SW_I2C_SDA_OUTPUT       nrf_gpio_cfg_output(gpio->SDA_Pin)                        // 将SDA脚方向设置为输出
#define     SW_I2C_SDA_STATUS       nrf_gpio_pin_read(gpio->SDA_Pin)                          // 获取SDA脚输入电平状态

#define     i2c_delay_us(a)         nrf_delay_us(a)     //延时


//一、I2C基本操作实现

#if 0
//1、SDA脚输入输出切换及输入状态读取

/**************************************************************************
*** 读取SDA脚的状态 ***
***************************************************************************/
static uint8_t sw_i2c_sda_status(sw_i2c_gpio_t* *gpio)
{
    uint8_t sda_status;

    sda_status = GPIO_ReadInputDataBit(gpio->SDA_Port,gpio->SDA_Pin);
    return sda_status?1:0;
}

/**************************************************************************
*** 设置SDA脚为输入 ***
***************************************************************************/
static void sw_i2c_set_sda_input(sw_i2c_gpio_t* *gpio)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    GPIO_InitStructure.GPIO_Pin = gpio->SDA_Pin;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入模式
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init (gpio->SDA_Port, & GPIO_InitStructure );
}

/**************************************************************************
*** 设置SDA脚为输出 ***
***************************************************************************/
static void sw_i2c_set_sda_output(sw_i2c_gpio_t* *gpio)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    GPIO_InitStructure.GPIO_Pin = gpio->SDA_Pin;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; //开漏输出模式
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init (gpio->SDA_Port, & GPIO_InitStructure );
}
#endif

//2、I2C启动

static void sw_i2c_start(sw_i2c_gpio_t* gpio)
{
    // I2C 开始时序：SCL=1时，SDA由1变成0.
    SW_I2C_SDA_HIGH;
//    i2c_delay_us(I2C_DELAY);
    SW_I2C_SCL_HIGH;
    i2c_delay_us(I2C_DELAY);
    SW_I2C_SDA_LOW;
    i2c_delay_us(I2C_DELAY);
    SW_I2C_SCL_LOW;
    i2c_delay_us(I2C_DELAY);
}


//3、I2C停止

static void sw_i2c_stop(sw_i2c_gpio_t* gpio)
{
    // I2C 开始时序：SCL=1时，SDA由0变成1.
    SW_I2C_SDA_LOW;
    i2c_delay_us(I2C_DELAY);
    SW_I2C_SCL_HIGH;
    i2c_delay_us(I2C_DELAY);
    SW_I2C_SDA_HIGH;
}


//4、等待数据接收方反馈ACK

static uint8_t sw_i2c_wait_ack(sw_i2c_gpio_t* gpio)
{
    uint8_t sda_status;
    uint8_t wait_time=0;
    uint8_t ack_nack = 1;

    //先设置SDA脚为输入
    SW_I2C_SDA_INPUT;
    
    //等待SDA脚被从机拉低
    while(SW_I2C_SDA_STATUS)
    {
        wait_time++;
        //如果等待时间过长，则退出等待
        if (wait_time>=200)
        {
            ack_nack = 0;
            break;
        }
    }
    
    // SCL由0变为1，读入ACK状态
    // 如果此时SDA=0，则是ACK
    // 如果此时SDA=1，则是NACK
    i2c_delay_us(I2C_DELAY);
    SW_I2C_SCL_HIGH;
    i2c_delay_us(I2C_DELAY);

    //再次将SCL=0，并且将SDA脚设置为输出
    SW_I2C_SCL_LOW;
    i2c_delay_us(I2C_DELAY);
    SW_I2C_SDA_OUTPUT;
    i2c_delay_us(I2C_DELAY);
    return ack_nack;
}


//5、发送ACK给数据发送方

static void sw_i2c_send_ack(sw_i2c_gpio_t* gpio)
{
    // 发送ACK就是在SDA=0时，SCL由0变成1
    SW_I2C_SDA_LOW;
    i2c_delay_us(I2C_DELAY);
    SW_I2C_SCL_HIGH;
    i2c_delay_us(I2C_DELAY);
    SW_I2C_SCL_LOW;
    i2c_delay_us(I2C_DELAY);
}


//6、发送NACK给数据发送方

static void sw_i2c_send_nack(sw_i2c_gpio_t* gpio)
{
    // 发送NACK就是在SDA=1时，SCL由0变成1
    SW_I2C_SDA_HIGH;
    i2c_delay_us(I2C_DELAY);
    SW_I2C_SCL_HIGH;
    i2c_delay_us(I2C_DELAY);
    SW_I2C_SCL_LOW;
    i2c_delay_us(I2C_DELAY);
}


//7、主设备向从设备写一个字节

static void sw_i2c_write_byte(sw_i2c_gpio_t* gpio, uint8_t aByte)
{
    uint8_t i;
    
    for (i = 0; i < 8; i++)
    {
        //先将SCL拉低；
        SW_I2C_SCL_LOW;
        i2c_delay_us(I2C_DELAY);
        
        //然后在SDA输出数据
        if(aByte&0x80)
        {
            SW_I2C_SDA_HIGH;
        }
        else
        {
            SW_I2C_SDA_LOW;
        }
        i2c_delay_us(I2C_DELAY);
        
        //最后将SCL拉高，在SCL上升沿写入数据
        SW_I2C_SCL_HIGH;
        i2c_delay_us(I2C_DELAY);

        aByte = aByte<<1;//数据位移
    }
    
    //写完一个字节只后要将SCL拉低
    SW_I2C_SCL_LOW;
    i2c_delay_us(I2C_DELAY);
}


//8、主设备从从设备读一个字节

static uint8_t sw_i2c_read_byte(sw_i2c_gpio_t* gpio)
{
    uint8_t i,aByte;

    //先将SDA脚设置为输入
    SW_I2C_SDA_INPUT;
    
    for (i = 0; i < 8; i++)
    {
        //数据位移
        aByte = aByte << 1;
        //延时等待SDA数据稳定
        i2c_delay_us(I2C_DELAY);
        
        //SCL=1，锁定SDA数据
        SW_I2C_SCL_HIGH;
        i2c_delay_us(I2C_DELAY);
        
        //读取SDA状态
        if(SW_I2C_SDA_STATUS)
        {
            aByte |= 0x01;
        }
        //SCL=0，解除锁定
        SW_I2C_SCL_LOW;
    }
    
    //读完一个字节，将SDA重新设置为输出
    SW_I2C_SDA_OUTPUT;
    return aByte;
}


//二、I2C传输数据函数实现

//1、模拟I2C初始化

void sw_i2c_init(sw_i2c_gpio_t* gpio)
{
    //SCL和SD均为输出高
    nrf_gpio_cfg_output(gpio->SCL_Pin);
    nrf_gpio_cfg_output(gpio->SDA_Pin);

    SW_I2C_SCL_HIGH;
    SW_I2C_SDA_HIGH;
}


//2、主设备向从设备写N个字节数据

uint8_t sw_i2c_write_nBytes(sw_i2c_gpio_t* gpio, uint8_t i2c_addr,uint8_t *data,uint8_t len)
{
    uint8_t err_code = 0;
    
    uint8_t j;

    //如果使用的是7bit地址，需要左位移一位
#ifdef I2C_USE_7BIT_ADDR
    i2c_addr = i2c_addr<<1;
#endif

    //启动I2C
    sw_i2c_start(gpio);
    
    //写I2C从机地址，写操作
    sw_i2c_write_byte(gpio, i2c_addr);
    //如果从机响应ACC则继续，如果从机未响应ACK则停止
    if (!sw_i2c_wait_ack(gpio))
    {
        err_code = 1;
        goto err;
    }

    //开始写n个字节数据
    for (j = 0; j < len; j++)
    {
        sw_i2c_write_byte(gpio, data[j]);
        // 每写一个字节数据后，都要等待从机回应ACK
        if (!sw_i2c_wait_ack(gpio))
        {
            err_code = 2;
            goto err;
        }
    }

    //停止I2C
    err:
    sw_i2c_stop(gpio);

    return err_code;
}


//3、主设备从从设备读取N个字节数据

void sw_i2c_read_nBytes(sw_i2c_gpio_t* gpio, uint8_t i2c_addr,uint8_t *buf,uint8_t len)
{
    uint8_t j;

    //如果使用的是7bit地址，需要左位移一位
#ifdef I2C_USE_7BIT_ADDR
    i2c_addr = i2c_addr<<1;
#endif

    //启动I2C
    sw_i2c_start(gpio);
    
    //写I2C从机地址，读操作
    sw_i2c_write_byte(gpio, i2c_addr|0x01);
    //如果从机响应ACC则继续，如果从机未响应ACK则停止
    if (!sw_i2c_wait_ack(gpio))
    {
        goto err;
    }
    
    //开始读n个字节数据
    for (j = 0; j < len; j++)
    {
        buf[j] = sw_i2c_read_byte(gpio);
        // 每读一个字节数据后，都要发送ACK给从机
        sw_i2c_send_ack(gpio);
    }

    //停止I2C
    err:
    sw_i2c_stop(gpio);
}


//4、主设备向从设备16Bit长度的寄存器地址读取N个字节

void sw_i2c_send2read_16bit(sw_i2c_gpio_t* gpio, uint8_t i2c_addr,uint16_t reg,uint8_t *buf,uint8_t len)
{
    uint8_t j;

    //如果使用的是7bit地址，需要左位移一位
#ifdef I2C_USE_7BIT_ADDR
    i2c_addr = i2c_addr<<1;
#endif

    //启动I2C
    sw_i2c_start(gpio);
    
    //写I2C从机地址，写操作
    sw_i2c_write_byte(gpio, i2c_addr);
    //如果从机响应ACC则继续，如果从机未响应ACK则停止
    if (!sw_i2c_wait_ack(gpio))
    {
        goto err;
    }

    //写寄存器地址高8位
    sw_i2c_write_byte(gpio, (reg >> 8) & 0xff);
    if (!sw_i2c_wait_ack(gpio))
    {
        goto err;
    }
    
    //写寄存器地址低8位
    sw_i2c_write_byte(gpio, reg&0xff);
    if (!sw_i2c_wait_ack(gpio))
    {
        goto err;
    }

    //重新启动I2C
    sw_i2c_start(gpio);
    
    //写I2C从机地址，读操作
    sw_i2c_write_byte(gpio, i2c_addr|0x01);
    if (!sw_i2c_wait_ack(gpio))
    {
        goto err;
    }
    
    //开始读n个字节数据
    for (j = 0; j < len; j++)
    {
        buf[j] = sw_i2c_read_byte(gpio);
        // 每读一个字节数据后，都要发送ACK给从机
        sw_i2c_send_ack(gpio);
    }
    
    //停止I2C
    err:
    sw_i2c_stop(gpio);
}


//5、主设备向从设备8Bit长度的寄存器地址读取N个字节

uint8_t sw_i2c_send2read_8bit(sw_i2c_gpio_t* gpio, uint8_t i2c_addr,uint8_t reg,uint8_t *buf,uint8_t len)
{
    uint8_t err_code = 0;

    uint8_t j;

    //如果使用的是7bit地址，需要左位移一位
#ifdef I2C_USE_7BIT_ADDR
    i2c_addr = i2c_addr<<1;
#endif

    //启动I2C
    sw_i2c_start(gpio);
    
    //写I2C从机地址，写操作
    sw_i2c_write_byte(gpio, i2c_addr);
    //如果从机响应ACC则继续，如果从机未响应ACK则停止
    if (!sw_i2c_wait_ack(gpio))
    {
        err_code = 1;
        goto err;
    }

    //写寄存器地址
    sw_i2c_write_byte(gpio, reg);
    if (!sw_i2c_wait_ack(gpio))
    {
        err_code = 2;
        goto err;
    }

    //重新启动I2C
    sw_i2c_start(gpio);
    //写I2C从机地址，读操作
    sw_i2c_write_byte(gpio, i2c_addr|0x01);
    if (!sw_i2c_wait_ack(gpio))
    {
        err_code = 3;
        goto err;
    }
    
    //开始读n个字节数据
    for (j = 0; j < len; j++)
    {
        buf[j] = sw_i2c_read_byte(gpio);
        
        // 每读一个字节数据后，都要发送ACK给从机
        if (j == len - 1)
        {
            sw_i2c_send_nack(gpio);
        }
        else
        {
            sw_i2c_send_ack(gpio);
        }
    }
    
    //停止I2C
    err:
    sw_i2c_stop(gpio);

    return err_code;
}


